/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * LocalValidatorUtil.java
 * 
 *                                   Revision History
 * -----------------------------------------------------------------------------------------
 * Author                    Date            CR        Comments
 * ----------------------|------------|--------------|--------------------------------------
 * pcmnac                  2007.10.27                  Initial version.
 * 
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

package com.google.code.localvalidator;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;

/**
 * Provides a set of utilitarian methods for local object validation.
 * 
 * @author pcmnac@gmail.com
 * 
 */
public class LocalValidatorUtil
{
  // ===========================================================================================================
  // Public API.
  // ===========================================================================================================

  /**
   * Validates the given validation cases. (Array version)
   * 
   * @param validationCases
   *          The validation cases to be validated.
   */
  public static void validate(final LocalValidationCase[] validationCases)
  {
    if (validationCases != null)
    {
      for (int i = 0; i < validationCases.length; i++)
      {
        if (!validationCases[i].isSuccessCondition())
        {
          if (validationCases[i].getException() != null)
          {
            throw validationCases[i].getException();
          }
          else
          {
            throw createResultException(validationCases[i].getExceptionClass(), validationCases[i]
                .getExceptionArguments(), i);
          }
        }
      }
    }
  }

  /**
   * Validates the given validation cases. (List version)
   * 
   * @param cases
   *          The validation cases to be validated.
   */
  public static void validate(final List<LocalValidationCase> cases) throws IllegalArgumentException
  {
    LocalValidationCase[] validationCases = new LocalValidationCase[cases.size()];
    cases.toArray(validationCases);
    validate(validationCases);
  }

  // ===========================================================================================================
  // Protected members.
  // ===========================================================================================================

  // ===========================================================================================================
  // Package members.
  // ===========================================================================================================

  // ===========================================================================================================
  // Private members.
  // ===========================================================================================================

  /**
   * Index mark (used in {@link #DEFAULT_MESSAGE}.
   */
  private static final String INDEX_MARK = "#";

  /**
   * The default exception message.
   */
  private static final String DEFAULT_MESSAGE = "Invalid argument [" + INDEX_MARK + "].";

  /**
   * Gets a default error message based on index.
   * 
   * @param invalidArgumentIndex
   *          The index of invalid argument.
   * 
   * @return The appropriate exception message.
   */
  private static String getErrorMessage(int invalidArgumentIndex)
  {
    return DEFAULT_MESSAGE.replaceAll(INDEX_MARK, Integer.toString(invalidArgumentIndex));
  }

  /**
   * Tries to create an instance of user given exception class. Case fail, a default IllegalArgumentException
   * will be created.
   * 
   * @param exceptionClass
   *          The user given exception class.
   * @param exceptionArguments
   *          The exception arguments.
   * @param index
   *          The validation case index (used for default message).
   * 
   * @return The user given exception or default IllegalArgumentException case fail.
   */
  private static RuntimeException createResultException(final Class<? extends RuntimeException> exceptionClass,
      Object[] exceptionArguments, final int index)
  {

    RuntimeException resultException = null;

    // If an exception class was provided...
    if (exceptionClass != null)
    {
      try
      {
        Constructor<? extends RuntimeException> constructor = null;

        // If any exception arguments ware passed...
        if (exceptionArguments != null && exceptionArguments.length > 0)
        {
          // Gets argument types.
          List<Class<?>> types = new ArrayList<Class<?>>();

          for (Object argument : exceptionArguments)
          {
            types.add(argument.getClass());
          }

          final Class<?> parameterTypes[] = new Class<?>[types.size()];
          types.toArray(parameterTypes);

          // Tries to get the constructor for the given arguments.
          try
          {
            constructor = exceptionClass.getConstructor(parameterTypes);
          }
          catch (NoSuchMethodException e)
          {
          }
        }

        // If the constructor was not found yet...
        if (constructor == null)
        {
          // Tries to get a constructor that expects a String argument.
          try
          {
            constructor = exceptionClass.getConstructor(String.class);
            exceptionArguments = new Object[] { getErrorMessage(index) };
          }
          catch (NoSuchMethodException e1)
          {
            // Tries to get the default constructor.
            constructor = exceptionClass.getConstructor();
            exceptionArguments = new Object[] {};
          }
        }

        // Makes the constructor accessible.
        constructor.setAccessible(true);

        // Creates the exception instance.
        resultException = constructor.newInstance(exceptionArguments);
      }
      catch (Exception e)
      {
      }
    }

    // If no exception class was passed or there was any during instance creation process...
    if (resultException == null)
    {
      // Creates a default IllegalArgumentException.
      resultException = new IllegalArgumentException(getErrorMessage(index));
    }

    return resultException;
  }
}
